package com.thoughtorigin.service {
import com.thoughtorigin.flux.Flux;

import com.thoughtorigin.meta.MetaOperator;
import com.thoughtorigin.module.ModuleLoader;

import com.thoughtorigin.util.Instantiator;

import flash.system.ApplicationDomain;

import org.flexunit.asserts.assertEquals;
import org.flexunit.asserts.assertNotNull;
import org.flexunit.asserts.assertNull;
import org.flexunit.asserts.assertTrue;
import org.flexunit.asserts.fail;
import org.mockito.integrations.any;
import org.mockito.integrations.atLeast;
import org.mockito.integrations.between;
import org.mockito.integrations.given;
import org.mockito.integrations.mock;
import org.mockito.integrations.useArgument;
import org.mockito.integrations.verify;

[RunWith("org.mockito.integrations.flexunit4.MockitoClassRunner")]
[Mock(type="com.thoughtorigin.service.Service")]
[Mock(type="com.thoughtorigin.flux.Flux")]
[Mock(type="com.thoughtorigin.util.Instantiator")]
[Mock(type="com.thoughtorigin.meta.MetaOperator")]
public class DictionaryServiceLocatorTest {

    private var _serviceLocator: DictionaryServiceLocator;

    [Before]
    public function setup(): void {
        _serviceLocator = new DictionaryServiceLocator();
    }

    [After]
    public function tearDown(): void {
        _serviceLocator = null;
    }

    [Test]
    public function shouldAddServices(): void {
        var service: Service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service1");
        _serviceLocator.addService(service);
        assertEquals(1, _serviceLocator.services.length);
        verify(atLeast(1)).that(service.serviceName);

        service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service2");
        _serviceLocator.addService(service);
        assertEquals(2, _serviceLocator.services.length);
        verify(atLeast(1)).that(service.serviceName);

    }

    [Test]
    public function shouldNotBeAbleToAddNullServices(): void {
        _serviceLocator.addService(null);
        assertEquals(0, _serviceLocator.services.length);
    }

    [Test]
    public function shouldOnlyHaveOneUniqueService(): void {
        var service: Service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service1");
        _serviceLocator.addService(service);
        assertEquals(1, _serviceLocator.services.length);
        verify(atLeast(1)).that(service.serviceName);

        service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service1");
        _serviceLocator.addService(service);
        assertEquals(1, _serviceLocator.services.length);
        verify(atLeast(1)).that(service.serviceName);
    }

    [Test]
    public function shouldRemoveServices(): void {
        var service: Service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service1");
        _serviceLocator.addService(service);
        assertEquals(1, _serviceLocator.services.length);
        verify(atLeast(1)).that(service.serviceName);

        service = mock(Service);
        assertNotNull(service);
        given(service.serviceName).willReturn("service2");
        _serviceLocator.addService(service);
        assertEquals(2, _serviceLocator.services.length);

        _serviceLocator.removeService(service);
        assertEquals(1, _serviceLocator.services.length);
        verify(atLeast(2)).that(service.serviceName);
    }

    [Test]
    public function shouldNotFailOnNullRemove(): void {
        try {
            _serviceLocator.removeService(null);
            assertTrue(true);
        } catch (e: TypeError) {
            fail();
        }
    }

    [Test]
    public function shouldLocateService(): void {
        var callback: Function = function(service: Service): void {
            assertNotNull(service);
            assertEquals(service2, service);
            callbackCalled = true;
        }

        var callbackCalled: Boolean = false;

        var service1: Service = mock(Service);
        given(service1.serviceName).willReturn("service1");
        var service2: Service = mock(Service);
        given(service2.serviceName).willReturn("service2");
        var service3: Service = mock(Service);
        given(service3.serviceName).willReturn("service3");

        _serviceLocator.addService(service1);
        _serviceLocator.addService(service2);
        _serviceLocator.addService(service3);

        _serviceLocator.locateService("service2", callback);

        assertTrue(callbackCalled);
    }

    [Test]
    public function shouldReturnNullIfServiceNotFound(): void {
        var callback: Function = function(service: Service): void {
            assertNull(service);
            callbackCalled = true;
        }

        var callbackCalled: Boolean = false;

        var service1: Service = mock(Service);
        given(service1.serviceName).willReturn("service1");
        var service2: Service = mock(Service);
        given(service2.serviceName).willReturn("service2");
        var service3: Service = mock(Service);
        given(service3.serviceName).willReturn("service3");

        _serviceLocator.addService(service1);
        _serviceLocator.addService(service2);
        _serviceLocator.addService(service3);

        _serviceLocator.locateService("", callback);

        assertTrue(callbackCalled);
    }

    [Test]
    public function shouldReturnServiceFromFlux(): void {
        var callback: Function = function(service: Service): void {
            assertNotNull(service);
            assertEquals(service1, service);
            callbackCalled = true;
        }

        var callbackCalled: Boolean = false;
        var flux: Flux = mock(Flux);
        var service1: Service = mock(Service);
        given(flux.getProperty("service1")).willReturn(service1);
        var metaOperator: MetaOperator = mock(MetaOperator);
        given(metaOperator.injectObject(any(), any())).will(useArgument(1).asFunctionAndCall(service1));
        given(service1.serviceName).willReturn("service1");

        _serviceLocator.flux = flux;
        _serviceLocator.metaOperator = metaOperator;
        _serviceLocator.locateService("service1", callback);

        if(!callbackCalled) {
            fail();
        }
    }

    [Test]
    public function shouldParseMetaDataWhenCreatingANewService(): void {
        var callback: Function = function(service: Service): void {
            assertNotNull(service);
            assertEquals(service1, service);
            callbackCalled = true;
            verify(between(1,1)).that(op.injectObject(any(), any()));
        }

        var callbackCalled: Boolean = false;
        var service1: Service = mock(Service);
        given(service1.serviceName).willReturn("service1");
        given(service1.createNew).willReturn(true);

        var instantiator: Instantiator = mock(Instantiator);
        given(instantiator.createByClassName(any(), any())).willReturn(service1);

        var op: MetaOperator = mock(MetaOperator);
        given(op.injectObject(any(), any())).will(useArgument(1).asFunctionAndCall(service1));
        _serviceLocator.metaOperator = op;

        _serviceLocator.addService(service1);
        _serviceLocator.instantiator = instantiator;
        _serviceLocator.locateService("service1", callback);

        if(!callbackCalled) {
            fail();
        }
    }
}
}
